Пользовательский стиль отображения

Вы можете создать свой собственный стиль отображения в OpenXava, просто добавив CSS файл.

Изменение стиля по умолчанию (начиная с версии 4.5)

Начиная с версии 4.5 вы можете добавлять и изменять стили в файле custom.css находящемся в web/xava/style.
Для того, чтобы перекрыть существующий стиль просто определите его вашем custom.css файле. Так, если вы добавите:
body{
font-size: 9px;
}
то вы измените font-size во всем вашем приложении (включая отображение в порталах).

Определение собственного стиля

Эти инструкции актуальны для версии 4.2 и выше, для более ранних версий смотрите примеры в OpenXavaTest/xava.properties
Для того, чтобы определить свой собственный стиль, добавьте следующие записи в xava.properties файл вашего проекта:
styleClass=org.openxava.web.style.Style
styleCSS=mystyle/mystyle.css
Затем создайте папку с названием mystyle внутри web/xava/style и добавьте файл mystyle.css в эту папку. В этом файле вы можете использовать следующие CSS классы: ox-module, ox-view, ox-detail, ox-action-link, ox-image-link, ox-button-bar, ox-button-bar-button, ox-active, ox-first, ox-last, ox-label, ox-module-description, ox-frame-content, ox-list, ox-list-header, ox-list-subheader, ox-list-pair, ox-list-odd, list-pair-selected, list-odd-selected, ox-list-info, ox-list-info-detail, ox-list-title, ox-header-list-count, ox-frame (new in v4.8, before it was frame), ox-frame-actions, ox-frame-title, ox-frame-title-label, ox-errors, ox-messages, ox-section, ox-section-tab, ox-active-section, ox-section-link, ox-collection-list-actions, ox-bottom-buttons, ox-page-navigation-selected, ox-page-navigation, ox-page-navigation-pages, ox-page-navigation-arrow, ox-page-navigation-arrow-disable, ox-images-gallery (new in v4.8), ox-help, ox-total-row, ox-total-cell, ox-total-capable-cell, ox-filter-cell, ox-frame-totals, ox-frame-totals-label, ox-frame-totals-value.
По желанию, вы можете создать свой собственный Style класс, расширяющий org.openxava.web.style.Style. Это позволяет вам еще лучше контролировать то, как выглядит и ведет себя ваше приложение.

Предопределенные стили

OpenXava предоставляется с несколькими заранее предопределенными стилями. Вы можете настраивать их в фале properties/xava.properties вашего проекта:
# styleClass and styleCSS have only effect outside a portal,
# inside a portal OX portlets acquire automatically the portal skin.
 
# Liferay 5.1: This is the DEFAULT, so you do not need to uncomment the next lines
#styleClass=org.openxava.web.style.Liferay51Style
#styleCSS=liferay51/css/everything_unpacked.css
 
# Liferay 4.3/4.4/5.0: uncomment the next line for use this nice and slow style
#styleClass=org.openxava.web.style.Liferay43Style
#styleCSS=liferay43/css/everything_unpacked.css
 
# Classic style: uncomment the next line for use the old OX style
#styleClass=org.openxava.web.style.PortalStyle
#styleCSS=default.css

В порталах

В порталах портлеты OpenXava автоматически подстраиваются под стиль портала, потому, если вы работаете с порталом, вам необходимо только создавать и менять стиль самого портала, а приложение OpenXava подстроится под него.
Дополнительно, если вы хотите использовать определенный стиль для портала, это можно сделать при помощи следующих свойств, доступных в xava.properties: liferay6StyleClass, liferay51StyleClass, liferay41StyleClass, liferay43StyleClass, webSpherePortal61StyleClass, webSpherePortal6StyleClass, webSpherePortalStyleClass, JetSpeed2Style.
Таким образом, если вы хотите создать ваш собственный стиль для приложения OpenXava, запускаемого в портале Liferay 6, просто добавьте следующую строку в xava.properties:
liferay6StyleClass=org.openxava.test.style.MyLiferay6Style
Понятно, что вам также необходимо описать MyLiferayStyle class.

Пример: стиль для iPad

Стиль для iPad по умолчанию включен в OpenXava и применяется автоматически, если пользователь обращается к приложению при помощи iPad.

Определение обозревателя/устройства

Для того, чтобы включить определение устройства, мы добавляем следующий метод в класс стиля:
public boolean isForBrowse(String browser) {
return browser != null && browser.contains("iPad");
}
Это способ сопоставления выбранного стиля определенному обозревателю или устройству. Вы можете использовать эту технику в вашем стиле, хотя при этом вам необходимо создать styles.properties файл в properties папке вашего проекта и добавить туда строку с классом вашего стиля, как в следующем примере:
com.mycompany.myapp.style.MyDeviceStyle
Кроме того, вам необходимо описать класс MyDeviceStyle with the c соответствующий isForBrowse() методом.

JavaScript код

Стиль для iPad также использует встроенные эффекты iPad такие как flip и slide. Чтобы этого достичь, необходимо использовать JavaScript код. В нашем случае мы определяем метод для определения способа, при помощи которого HTML token полученный при помощи AJAX с сервера, связывается с элементом HTML документа:
public String getSetHtmlFunction() {
return "ipad.setHtml";
}
Таким образом, мы указываем, что, вместо стандартной, необходимо использовать функцию ipad.setHtml. Эта функция поределена в ipad.js файле. Мы должны также указать это классу стиля, который загружает этот JavaScript файл:
private static String [] jsFiles = {
"ipad/ipad.js",
};
 
public String [] getNoPortalModuleJsFiles() {
return jsFiles;
}
Это сам ipad.js файл в папке web/style/ipad:
if (ipad == null) var ipad = {};
 
ipad.onLoad = function() {
var id = $('body').data('ipad-slide-element');
var clazz = $('body').data('ipad-slide-class');
$('#' + id).addClass(clazz);
setTimeout("$('#" + id + "').removeClass('" + clazz + "')", 0);
$('body').removeData();
}
 
ipad.onClickNextPage = function(listId) {
$('#' + listId).addClass('slided-left');
$('body').data('ipad-slide-element', listId);
$('body').data('ipad-slide-class', 'slided-right');
}
 
ipad.onClickPreviousPage = function(listId) {
$('#' + listId).addClass('slided-right');
$('body').data('ipad-slide-element', listId);
$('body').data('ipad-slide-class', 'slided-left');
}
 
ipad.setHtml = function(id, content) {
if (!id.match(/__core$/)) {
$("#" + id).html(content);
return;
}
 
// Core
$("#container").addClass("container");
$("#sheet").addClass("sheet");
$("#front").addClass("face");
$("#back").addClass("face");
 
if ($("#sheet").hasClass("flipped")) {
$("#" + id).html(content);
$("#sheet").removeClass("flipped");
}
else {
$("#core_BACK").html(content);
$("#sheet").addClass("flipped");
}
 
$('#sheet').bind('webkitTransitionEnd',
function( event ) {
if ($("#sheet").hasClass("flipped")) $("#" + id).empty();
else {
$("#core_BACK").empty();
$("#container").removeClass("container");
$("#sheet").removeClass("sheet");
$("#front").removeClass("face");
$("#back").removeClass("face");
}
$('#sheet').unbind();
},
false
);
 
}

CSS файл

Ниже приведен полный ipad.css файл. Этот файл вы можете найти в дистрибутиве OpenXava, в папке OpenXava/web/style/ipad.
body {
margin: 0px; background: black;
}
 
#xava_loading, #xava_loading2 {
padding: 20px;
border: none;
background: -webkit-gradient(linear, left bottom, left top,
from(#10182C), color-stop(0.5, #121A2E),
color-stop(0.51, #282F41), to(#7E838D));
opacity:.60;
-webkit-border-radius: 7px;
color: white;
text-shadow:0px -1px 1px rgba(0,0,0,0.75);
font-weight: bold;
-webkit-box-shadow: 10px 10px 10px rgba(140,140,140,0.75);;
display:none;
z-index: 9999;
position: fixed;
left: 45%
}
 
#xava_loading {
top: 25%;
}
 
#xava_loading2 {
top: 120%;
}
 
.ox-module {
font-family: Helvetica, Arial, sans-serif;
}
 
 
/* Portrait */
@media screen and (orientation:portrait) {
.ox-module, .ui-widget-content, #xava_loading, #xava_loading2 {
font-size: 1.2em;
}
.ui-widget-content {
font-size: 1.1em;
}
.ui-widget-header {
font-size: 180%;
}
.ox-button-bar, .ox-section {
height: 32px;
}
.ox-page-navigation-pages {
top: 8px;
}
.ox-bottom-buttons input[type="button"] {
-webkit-box-shadow: 0px 2px 0px white;
}
.ox-detail, .ox-frame-content, .ox-active-section {
border: 2px solid #B2B4B8;
-webkit-box-shadow: 0px 2px 0px white;
}
.ox-frame-actions img {
height: 38px;
}
}
 
 
 
/* Landscape */
@media screen and (orientation:landscape) {
.ox-module {
font-size: 0.9em;
}
.ui-widget-content, #xava_loading, #xava_loading2 {
font-size: 0.95em;
}
.ui-widget-header {
font-size: 150%;
}
.ox-button-bar, .ox-section {
border-bottom: none;
height: 25px;
}
.ox-page-navigation-pages {
top: 11px;
}
.ox-bottom-buttons input[type="button"] {
-webkit-box-shadow: 0px 1px 0px white;
}
.ox-button-bar-image img {
height: 19px;
}
.ox-detail, .ox-frame-content, .ox-active-section {
border: 1px solid #B2B4B8;
border-top: 1px solid #8F8F8F;
-webkit-box-shadow: 0px 1px 0px white;
}
}
 
.ox-view {
background: #D8D8D9;
padding: 20px;
border-bottom: 1px solid #959596;
}
 
.ox-detail {
font-size: 130%;
 
}
 
.ox-detail, .ox-frame-content, .ox-active-section {
background: white;
padding: 10px;
-webkit-border-radius:15px;
color: #385587;
text-shadow: 0px 1px 1px black #A0ADC4;
}
 
.ox-active-section {
-webkit-border-top-left-radius: 0px;
-webkit-border-top-right-radius: 0px;
border-left: 1px solid #B2B4B8;
border-right: 1px solid #B2B4B8;
}
 
.ox-frame-title-label {
padding: 10px 0px 0px 10px;
vertical-align: bottom;
}
 
input {
color: #385587;
}
 
.ox-label {
text-align: left;
color: black;
font-weight: bold;
text-shadow: 0px 2px 1px #E8E8E9;
}
 
.ox-button-bar, .ox-collection-list-actions, .ox-section {
font-size: 0.9em;
padding: 10px 4px 0px 6px;
border-top: 1px solid white;
-webkit-border-top-left-radius: 7px;
-webkit-border-top-right-radius: 7px;
background: white;
background: -webkit-gradient(linear, left bottom, left top,
from(#A8ADB9), to(#F5F6F7));
}
.ox-collection-list-actions {
padding: 0px 4px 0px 6px;
height: 40px;
}
 
.ox-detail .ox-action-link {
background: white;
padding: 5px;
border: 1px solid #C4C4C4;
-webkit-border-radius:5px;
color: #7C7A77;
font-size: 0.9em;
text-decoration: none;
text-shadow:1px 0px 0px #EDEDED;
}
 
.ox-button-bar-button a, .ox-list .ox-action-link {
margin-right: 5px;
padding: 4px 9px 5px;
border: 1px solid #51555B;
border-top: 1px solid #4C4E50;
-webkit-border-radius:5px;
background: -webkit-gradient(linear, left bottom, left top,
from(#6B737E), to(#B2B5BB));
color: white;
font-weight: bold;
text-decoration:none;
text-shadow:0px -1px 1px rgba(0,0,0,0.75);
}
 
.ox-button-bar-image {
padding: 0px 15px 5px;
vertical-align: top;
}
 
.ox-button-bar-mode-button, .ox-section-tab, .ox-section-link {
padding: 4px 9px 5px;
border: 1px solid #51555B;
border-right: none;
border-left: none;
border-top: 1px solid #4C4E50;
background: -webkit-gradient(linear, left bottom, left top,
from(#A8ADB9), to(#F5F6F7));
font-weight: bold;
text-decoration:none;
color: #5D6065;
text-shadow:0px 1px 1px white;
}
 
.ox-active .ox-button-bar-mode-button, .ox-active .ox-section-tab {
background: -webkit-gradient(linear, left bottom, left top,
from(#6B737E), to(#B2B5BB));
color: white;
text-shadow:0px -1px 1px rgba(0,0,0,0.75);
}
 
.ox-first .ox-button-bar-mode-button, .ox-first .ox-section-tab {
border-left: 1px solid #51555B;
-webkit-border-top-left-radius: 5px;
-webkit-border-bottom-left-radius: 5px;
}
 
.ox-last .ox-button-bar-mode-button, .ox-last .ox-section-tab {
border-right: 1px solid #51555B;
-webkit-border-top-right-radius: 5px;
-webkit-border-bottom-right-radius: 5px;
}
 
.ox-list {
font-size: 0.9em;
border: none;
padding: 2px;
-webkit-border-radius:7px;
-webkit-box-shadow: inset 1px 1px 4px #4E4E4E;
color: #5D6065;
font-weight: bold;
text-shadow:0px 1px 1px white;
background: #D8D8D9;
-webkit-transition-property: -webkit-transform;
-webkit-transition-duration: 1.5s;
}
 
.slided-left {
-webkit-transform: translateX(-130%);
}
 
.slided-right {
-webkit-transform: translateX(200%);
}
 
 
.ox-list-odd {
background: #BBBBBB;
border-top: 1px solid #CFCFCF;
border-bottom: 1px solid #959596;
padding: 10px 8px 10px 8px;
}
 
.ox-list-pair {
background: #AFAFAF;
border-top: 1px solid #C6C6C6;
border-bottom: 1px solid #959596;
padding: 10px 8px 10px 8px;
}
 
.ox-total-row {
height: 40px;
background: #AFAFAF;
border-top: 1px solid #C6C6C6;
border-bottom: 1px solid #959596;
background: -webkit-gradient(linear, left bottom, left top,
from(#565A63), color-stop(0.5, #6A6E76),
color-stop(0.51, #797D84), to(#91969E));
}
 
.ox-total-cell, .ox-total-capable-cell {
text-align: right;
color: white;
text-shadow:0px -1px 1px rgba(0,0,0,0.75);
}
 
.ox-list-header a {
color: white;
text-shadow:0px -1px 1px rgba(0,0,0,0.75);
}
 
.ox-list-header {
background: -webkit-gradient(linear, left bottom, left top,
from(#797D84), to(#91969E));
}
 
.ox-list-subheader {
background: -webkit-gradient(linear, left bottom, left top,
from(#565A63), to(#6A6E76));
padding: 2px 8px 2px 10px;
}
 
.ox-list-title, .ox-module-description {
font-weight: bold;
font-size: 140%;
color: #2C2F33;
text-shadow:0px 2px 1px #E8E8E9;
padding-left: 12px;
padding-bottom: 12px;
}
 
.ox-module-description {
color: #385587;
text-shadow: 0px 1px 1px white;
 
}
 
.ox-header-list-count {
margin-left: 10px;
font-size: 75%;
}
 
.ox-list-info {
margin-top: 10px;
-webkit-border-radius:7px;
border: 1px solid #505153;
background: -webkit-gradient(linear, left bottom, left top,
from(#565A63), color-stop(0.5, #6A6E76),
color-stop(0.51, #797D84), to(#91969E));
}
 
.ox-page-navigation-arrow-disable, .ox-page-navigation-arrow img {
padding: 5px 10px 7px 10px;
}
 
.ox-first .ox-page-navigation-arrow, .ox-first .ox-page-navigation-arrow-disable {
border-right: 2px ridge #878B91;
float: left;
}
 
.ox-last .ox-page-navigation-arrow, .ox-last .ox-page-navigation-arrow-disable {
border-left: 2px groove #878B91;
float: right;
}
 
.ox-page-navigation-pages {
float:left;
position:relative;
left:40%;
}
 
.ox-page-navigation, .ox-page-navigation-selected {
margin-right: 10px;
}
 
.ox-bottom-buttons {
border-top: 1px solid white;
background: #D8D8D9;
padding: 10px;
text-align: center;
}
 
.ox-bottom-buttons input[type="button"] {
padding: 20px;
margin: 10px;
border: 1px solid #979797;
-webkit-border-radius:10px;
background: -webkit-gradient(linear, left bottom, left top,
from(#D9D9D9), to(white));
color: black;
font-weight: bold;
font-size: 110%;
}
 
.ox-errors {
color: #CF0000;
font-weight: bold;
font-size: 140%;
text-shadow:0px 2px 1px #E8E8E9;
}
 
.ox-messages {
color: #2160DD;
font-weight: bold;
font-size: 140%;
text-shadow:0px 2px 1px #E8E8E9;
}
 
.ox-filter-cell {
text-align: left;
}
 
.ox-frame-totals {
float: right;
}
.ox-frame-totals-label {
font-weight: normal;
}
 
.ox-frame-totals-value {
font-weight: bold;
}
 
.ui-widget-header {
border: none;
background: -webkit-gradient(linear, left bottom, left top,
from(#10182C), color-stop(0.5, #121A2E),
color-stop(0.51, #282F41), to(#7E838D));
color: white;
text-shadow:0px -1px 1px rgba(0,0,0,0.75);
font-weight: bold;
}
 
.ui-corner-all {
-webkit-border-radius: 10px;
}
 
.ui-widget-content {
border: none;
background: #151D31;
-webkit-box-shadow: 15px 15px 30px rgba(140,140,140,0.75);;
}
 
.ui-widget-content .ox-view {
-webkit-border-top-left-radius: 7px;
-webkit-border-top-right-radius: 7px;
}
.ui-widget-content .ox-bottom-buttons {
-webkit-border-bottom-left-radius: 7px;
-webkit-border-bottom-right-radius: 7px;
}
 
.ui-dialog .ui-dialog-content {
padding: 5px;
}
 
 
/* For flip effect */
.container {
-webkit-perspective: 600;
}
 
 
.sheet {
position: absolute;
width: 100%;
}
 
 
.face {
position: absolute;
width: 100%;
overflow: hidden;
}
 
 
.sheet {
-webkit-transform-style: preserve-3d;
-webkit-transition-property: -webkit-transform;
-webkit-transition-duration: 1.5s;
}
 
 
.flipped
{
-webkit-transform: rotateY(180deg);
}
 
#front, #back {
-webkit-backface-visibility: hidden;
}
 
#back {
-webkit-transform: rotateY(180deg);
}

Класс стиля

Ниже приведен код класса IPadStyle. Вы можете найти этот класс в дистрибутиве OpenXava, в OpenXava/src/org.openxava.web.style.
package org.openxava.web.style;
 
/**
* For iPads. <p>
*
* @author Javier Paniza
*/
 
public class IPadStyle extends Style {
 
private static String [] jsFiles = {
"ipad/ipad.js",
};
 
private static IPadStyle instance = null;
 
protected IPadStyle() {
}
 
public static Style getInstance() {
if (instance == null) {
instance = new IPadStyle();
}
return instance;
}
 
public boolean isForBrowse(String browser) {
return browser != null && browser.contains("iPad");
}
 
public boolean isOnlyOneButtonForModeIfTwoModes() {
return true;
}
 
public boolean isSeparatorBeforeBottomButtons() {
return false;
}
 
public String getSetHtmlFunction() {
return "ipad.setHtml";
}
 
 
public String getCssFile() {
return "ipad/ipad.css";
}
 
public String [] getNoPortalModuleJsFiles() {
return jsFiles;
}
 
 
public String getInitThemeScript() {
return "ipad.onLoad()";
}
 
public String getNextPageNavigationEvents(String listId) {
return "onclick=ipad.onClickNextPage('" + listId + "')";
}
 
public String getPreviousPageNavigationEvents(String listId) {
return "onclick=ipad.onClickPreviousPage('" + listId + "')";
}
 
public String getCoreStartDecoration() {
return "<div id='container'><div id='sheet'><div id='front'>";
}
 
public String getCoreEndDecoration() {
return
"</div><div id='back'><div id='core_BACK' style='display: inline;' class='"
+ getModule() + "'></div></div></div></div>";
}
 
public String getDefaultModeController() {
return "DetailList";
}
 
public boolean allowsResizeColumns() {
return false;
}
 
public boolean isRowLinkable() {
return false;
}
 
public boolean isShowPageNumber() {
return false;
}
 
public boolean isShowModuleDescription() {
return true;
}
 
public boolean isSeveralActionsPerRow() {
return false;
}
 
public boolean isChangingPageRowCountAllowed() {
return false;
}
 
public boolean isHideRowsAllowed() {
return false;
}
 
public boolean isShowRowCountOnTop() {
return true;
}
 
public boolean isUseLinkForNoButtonBarAction() {
return true;
}
 
public boolean isHelpAvailable() {
return false;
}
 
public boolean isShowImageInButtonBarButton() {
return false;
}
 
public boolean isUseStandardImageActionForOnlyImageActionOnButtonBar() {
return true;
}
 
public boolean isFixedPositionSupported() {
return false;
}
 
 
public String getMetaTags() {
return "<meta name='apple-mobile-web-app-capable' content='yes'/>";
}
 
 
public String getModuleSpacing() {
return "style='padding: 0px;'";
}
 
public String getListCellSpacing() {
return "cellspacing=0 cellpadding=0";
}
 
public String getImagesFolder() {
return "xava/style/ipad/images";
}
 
}